import type { Metadata } from 'next'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import { and, eq } from '@/lib/db'; import { db, collections, collectionItems } from '@/lib/db'; import { requireUser } from '@/lib/auth/session'; import { getAssetBrief, listCollections, loadItems } from '@/lib/account/queries'; import { valueItem } from '@/lib/account/portfolio'; import { getDisplay } from '@/lib/account/display'; import { deleteItemAction, moveItemAction, removeItemPhotoAction } from '@/lib/account/actions'; import { PageHeader, btnDanger, btnSecondary } from '@/components/account/page-header'; import { ValueSourceBadge } from '@/components/account/portfolio-widgets'; import { Card, CardHeader, Delta, Stat } from '@/components/ui/primitives'; import { confidenceLabel, fmtPct } from '@/lib/format'; import { EditItemForm } from '../../add-item-form'; import { PhotoUploadForm } from './photo-form'; export const metadata: Metadata = { title: 'Item', robots: { index: false } }; export default async function ItemPage({ params }: { params: Promise<{ id: string; itemId: string }> }) { const { id, itemId } = await params; const u = await requireUser(`/collections/${id}/items/${itemId}`); const own = await db().select({ id: collectionItems.id }).from(collectionItems).innerJoin(collections, eq(collections.id, collectionItems.collectionId)).where(and(eq(collectionItems.id, itemId), eq(collections.id, id), eq(collections.userId, u.id))).limit(1); if (!own[0]) notFound(); const loaded = (await loadItems([id])).find((i) => i.id === itemId); if (!loaded) notFound(); const v = valueItem(loaded); const brief = await getAssetBrief(loaded.assetId); const d = await getDisplay(); const others = (await listCollections(u.id)).filter((c) => c.collection.id !== id); return ( <> Collections {' '} /{' '} Collection {' '} / {loaded.title} {loaded.categorySlug.replace(/_/g, ' ')} · {' '} Open asset page → > } actions={ <> {others.length ? ( Move to… {others.map((o) => ( {o.collection.name} ))} Move ) : null} Remove item > } /> } sub={v.gainUsd === null ? '—' : d.money(v.gainUsd)} /> {brief?.stats ? ( Asset RIV {brief.stats.rivUsd ? `${d.money(brief.stats.rivUsd)} (${brief.stats.rivSampleSize} sales)` : '—'} Latest sale {brief.stats.latestSaleUsd ? d.money(brief.stats.latestSaleUsd) : '—'} 30d / 1y {fmtPct(brief.stats.change30d)} / {fmtPct(brief.stats.change1y)} Liquidity · rarity {brief.stats.liquidityScore ?? '—'} · {brief.stats.rarityScore ?? '—'} Active listings {brief.stats.activeListings}{brief.stats.minAskUsd ? ` · min ask ${d.money(brief.stats.minAskUsd)}` : ''} Sales 30d {brief.stats.sales30d} ) : null} {loaded.photos.length ? ( {loaded.photos.map((p) => ( {/* eslint-disable-next-line @next/next/no-img-element */} ✕ ))} ) : ( No photos yet. Photos stay private unless the collection is public. )} > ); }
No photos yet. Photos stay private unless the collection is public.